﻿using System.Collections;
using System.Collections.Generic;

//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2021 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

namespace shaco.Base
{
    public class ObjectPool : shaco.Base.IObjectPool
    {
        private class ObjectReferenceComparer : IEqualityComparer<object>
        {
            new public bool Equals(object x, object y)
            {
                return System.Object.Equals(x, y);
            }

            public int GetHashCode(object obj)
            {
                return obj.GetHashCode();
            }
        }

        //内存池堆栈信息临时开关，以防止在某些地方过多调用堆栈信息获取导致cpu占用过高的情况
        public bool isOpendStackLocation { get; set; }

        //已经实例化的对象数量
        public int instantiatedCount { get { return _instantiateObjectToKeys.Count; } }

        //尚未实例化使用的对象数量
        public int unsuedCount { get { return _unUsedObjectToKeys.Count; } }

        private Dictionary<string, List<PoolDataInfo>> _objectsUnusedPool = new Dictionary<string, List<PoolDataInfo>>();
        private Dictionary<string, List<PoolDataInfo>> _objectsInstantiatePool = new Dictionary<string, List<PoolDataInfo>>();
        private Dictionary<object, string> _instantiateObjectToKeys = new Dictionary<object, string>(new ObjectReferenceComparer());
        private Dictionary<object, string> _unUsedObjectToKeys = new Dictionary<object, string>(new ObjectReferenceComparer());

        private System.Threading.Mutex _mutex = new System.Threading.Mutex();
        private Stack<List<PoolDataInfo>> _cachePoolDataInfo = new Stack<List<PoolDataInfo>>();

        //遍历内存缓存池
        public void ForeacUnusedPool(System.Func<string, List<PoolDataInfo>, bool> callback)
        {
            lock (_mutex)
            {
                bool result = true;
                foreach (var item in _objectsUnusedPool)
                {
                    try
                    {
                        result = callback(item.Key, item.Value);
                    }
                    catch (System.Exception e)
                    {
                        Log.Error("ObjectPool ForeacUnusedPool error: callback=" + callback + " e=" + e);
                        result = false;
                    }
                    if (!result)
                        break;
                }
            }
        }

        //遍历内存实例化池
        public void ForeachInstantiatePool(System.Func<string, List<PoolDataInfo>, bool> callback)
        {
            lock (_mutex)
            {
                bool result = true;
                foreach (var item in _objectsInstantiatePool)
                {
                    try
                    {
                        result = callback(item.Key, item.Value);
                    }
                    catch (System.Exception e)
                    {
                        Log.Error("ObjectPool ForeacUnusedPool error: callback=" + callback + " e=" + e);
                        result = false;
                    }
                    if (!result)
                        break;
                }
            }
        }

        //清理没有使用的缓存对象
        public void UnloadUnusedPoolData()
        {
            lock (_mutex)
            {
                foreach (var iter in _objectsUnusedPool)
                {
                    foreach (var obj in iter.Value)
                        GetObjectSpawn().DestroyObject(obj.value);
                }
                _objectsUnusedPool.Clear();
                _unUsedObjectToKeys.Clear();
                _cachePoolDataInfo.Clear();
            }
            System.GC.Collect();
        }

        //从对象池中实例化一个对象，默认使用T类型名字作为key
        public T Instantiate<T>(in System.Func<T> callbackCreate)
        {
            return InstantiateWithKey(null, in callbackCreate);
        }

        //从对象池中实例化一个对象
        public T InstantiateWithKey<T>(string key, in System.Func<T> callbackCreate)
        {
            lock (_mutex)
            {
                T retValue = default(T);
                PoolDataInfo instantiatePoolData = null;

                if (null == key)
                {
                    key = typeof(T).FullName;
                }

                List<PoolDataInfo> findObjectsList = null;
                if (!_objectsUnusedPool.TryGetValue(key, out findObjectsList))
                {
                    instantiatePoolData = new PoolDataInfo();
                    if (isOpendStackLocation)
                    {
                        instantiatePoolData.CheckStackValid();
                        instantiatePoolData.stackLocationInstantiate.StartTimeSpanCalculate();
                    }

                    retValue = GetObjectSpawn().CreateNewObject<T>(callbackCreate);
                    if (null == retValue)
                    {
                        Log.Error("ObjectPool InstantiateBase error: create value is null, type=" + key + " key=" + key);
                    }
                    else
                    {
                        instantiatePoolData.value = retValue;
                        InstantiateBaseEnd(key, instantiatePoolData);
                    }

                    if (isOpendStackLocation)
                    {
                        instantiatePoolData.stackLocationInstantiate.GetStack();
                        instantiatePoolData.stackLocationInstantiate.StopTimeSpanCalculate();
                    }
                }
                else
                {
                    instantiatePoolData = findObjectsList[findObjectsList.Count - 1];

                    if (isOpendStackLocation)
                    {
                        instantiatePoolData.CheckStackValid();
                        instantiatePoolData.stackLocationInstantiate.StartTimeSpanCalculate();
                    }
                    retValue = (T)instantiatePoolData.value;

                    var removedPoolData = RemoveObjectFromPool(_objectsUnusedPool, key);
                    RemoveObjectToKeyReference(_unUsedObjectToKeys, removedPoolData);
                    GetObjectSpawn().ActiveObject(retValue);

                    InstantiateBaseEnd(key, instantiatePoolData);

                    if (isOpendStackLocation)
                    {
                        instantiatePoolData.stackLocationInstantiate.GetStack();
                        instantiatePoolData.stackLocationInstantiate.StopTimeSpanCalculate();
                    }
                }
                return retValue;
            }
        }

        //从对象池中异步实例化一个对象，默认使用T类型名字作为key
        public void InstantiateAsync<T>(in System.Action<System.Action<T>> callbackCreate, in System.Action<T> callbackEnd)
        {
            this.InstantiateAsyncWithKey<T>(null, in callbackCreate, in callbackEnd);
        }

        public void InstantiateAsyncWithKey<T>(string key, in System.Action<System.Action<T>> callbackCreate, in System.Action<T> callbackEnd)
        {
            lock (_mutex)
            {
                if (null == key)
                {
                    key = typeof(T).FullName;
                }

                List<PoolDataInfo> findObjectsList = null;
                if (_objectsUnusedPool.TryGetValue(key, out findObjectsList))
                {
                    InstantiateAsyncWithCache<T>(key, in findObjectsList, in callbackCreate, in callbackEnd);
                }
                else
                {
                    InstantiateAsyncWithNew<T>(key, in callbackCreate, in callbackEnd);
                }
            }
        }

        //从对象池中异步实例化一个对象，默认使用T类型名字作为key
        public IEnumeratorRequest<T> InstantiateCoroutine<T>(System.Action<System.Action<T>> callbackCreate)
        {
            return InstantiateCoroutineWithKey<T>(null, callbackCreate);
        }

        //从对象池中异步实例化一个对象
        public IEnumeratorRequest<T> InstantiateCoroutineWithKey<T>(string key, System.Action<System.Action<T>> callbackCreate)
        {
            var retValue = new EnumeratorRequest<T>();
            GameHelper.objectpool.InstantiateAsync(callbackCreate, (obj) =>
            {
                retValue.SetResult(obj);
            });
            return retValue;
        }

        //预加载一个对象，并自动放到未使用缓存池中
        public T[] PreLoad<T>(string key, int preloadCount) where T : new()
        {
            var retValue = new T[preloadCount];
            for (int i = 0; i < preloadCount; ++i)
            {
                var instantiateTmp = InstantiateWithKey(key, () => new T());
                RecyclingObject(instantiateTmp);
                retValue[i] = instantiateTmp;
            }
            return retValue;
        }

        //预加载一个对象，并自动放到未使用缓存池中
        public T[] PreLoadWithCustomCreate<T>(string key, System.Func<T> callbackCreate, int preloadCount)
        {
            var retValue = new T[preloadCount];
            for (int i = 0; i < preloadCount; ++i)
            {
                var instantiateTmp = InstantiateWithKey<T>(key, callbackCreate);
                RecyclingObject(instantiateTmp);
                retValue[i] = instantiateTmp;
            }
            return retValue;
        }

        /// <summary>
        /// 回收key中所有内存对象信息
        /// <param name="key">对象key值</param>
        /// <return>已经回收的对象组</return>
        /// </summary>
        public void RecyclingAllObjects(string key)
        {
            lock (_mutex)
            {
                var poolDatas = RemoveObjectsFromPool(_objectsInstantiatePool, key, true);
                RemoveObjectsToKeyReference(_instantiateObjectToKeys, poolDatas);

                if (!poolDatas.IsNullOrEmpty())
                {
                    AddObjectsToPool(_objectsUnusedPool, key, poolDatas);
                    UpdateObjectToKeyReferences(_unUsedObjectToKeys, key, poolDatas);
                    for (int i = 0; i < poolDatas.Count; ++i)
                    {
                        if (isOpendStackLocation)
                        {
                            poolDatas[i].CheckStackValid();
                            poolDatas[i].stackLocationRecycling.StartTimeSpanCalculate();
                        }
                        CheckObjectPoolDataOnRecycle(poolDatas[i].value);
                        GetObjectSpawn().RecyclingObject(poolDatas[i].value);

                        if (isOpendStackLocation)
                        {
                            poolDatas[i].stackLocationRecycling.GetStack();
                            poolDatas[i].stackLocationRecycling.StopTimeSpanCalculate();
                        }
                    }
                }

                poolDatas.Clear();
                _cachePoolDataInfo.Push(poolDatas);
            }
        }

        /// <summary>
        /// 回收单个对象
        /// <param name="obj">对象</param>
        /// <return>已经回收的对象</return>
        /// </summary>
        public object RecyclingObject(object obj)
        {
            lock (_mutex)
            {
                var key = ObjectToKey(obj);
                var retValue = RemoveObjectFromPool(_objectsInstantiatePool, key, obj, true);
                RemoveObjectToKeyReference(_instantiateObjectToKeys, retValue);

                if (null != retValue && null != retValue.value)
                {
                    if (isOpendStackLocation)
                    {
                        retValue.CheckStackValid();
                        retValue.stackLocationRecycling.StartTimeSpanCalculate();
                    }
                    AddObjectToPool(_objectsUnusedPool, key, retValue);
                    UpdateObjectToKeyReference(_unUsedObjectToKeys, key, retValue);
                    CheckObjectPoolDataOnRecycle(obj);
                    GetObjectSpawn().RecyclingObject(obj);

                    if (isOpendStackLocation)
                    {
                        retValue.stackLocationRecycling.GetStack();
                        retValue.stackLocationRecycling.StopTimeSpanCalculate();
                    }
                }
            }
            return obj;
        }

        /// <summary>
        /// 销毁key中所有对象
        /// <param name="key">对象key值</param>
        /// </summary>
        public void DestroyAllObjects(string key)
        {
            lock (_mutex)
            {
                var poolDatasWithInstantiate = RemoveObjectsFromPool(_objectsInstantiatePool, key, false);
                bool destorySuccess = false;
                RemoveObjectsToKeyReference(_instantiateObjectToKeys, poolDatasWithInstantiate);
                if (null != poolDatasWithInstantiate)
                {
                    for (int i = 0; i < poolDatasWithInstantiate.Count; ++i)
                    {
                        var valueTmp = poolDatasWithInstantiate[i].value;
                        if (null == valueTmp)
                        {
                            Log.Error("ObjectPool DestroyAllObjects error: instantiated object is null, key=" + key + " index=" + i);
                            continue;
                        }
                        CheckObjectPoolDataOnRecycle(valueTmp);
                        GetObjectSpawn().DestroyObject(valueTmp);
                        destorySuccess = true;
                    }
                    poolDatasWithInstantiate.Clear();
                    _cachePoolDataInfo.Push(poolDatasWithInstantiate);
                }

                var poolDatasWithUnused = RemoveObjectsFromPool(_objectsUnusedPool, key, false);
                RemoveObjectsToKeyReference(_unUsedObjectToKeys, poolDatasWithUnused);
                if (null != poolDatasWithUnused)
                {
                    for (int i = 0; i < poolDatasWithUnused.Count; ++i)
                    {
                        var valueTmp = poolDatasWithUnused[i].value;
                        if (null == valueTmp)
                        {
                            Log.Error("ObjectPool DestroyAllObjects error: unused object is null, key=" + key + " index=" + i);
                            continue;
                        }
                        CheckObjectPoolDataOnRecycle(valueTmp);
                        GetObjectSpawn().DestroyObject(valueTmp);
                        destorySuccess = true;
                    }
                    poolDatasWithUnused.Clear();
                    _cachePoolDataInfo.Push(poolDatasWithUnused);
                }

                if (!destorySuccess)
                {
                    Log.Error("ObjectPool DestroyAllObjects error: not found object in pool, key=" + key);
                }
            }
        }

        public object DestroyObject(object obj)
        {
            lock (_mutex)
            {
                var key = ObjectToKey(obj);
                var instantiateInfo = RemoveObjectFromPool(_objectsInstantiatePool, key, obj, false);
                bool destorySuccess = false;

                if (null != instantiateInfo)
                {
                    var objsWithInstantiate = instantiateInfo.value;
                    RemoveObjectToKeyReference(_instantiateObjectToKeys, instantiateInfo);
                    if (null != objsWithInstantiate)
                    {
                        destorySuccess = true;
                        GetObjectSpawn().DestroyObject(objsWithInstantiate);
                    }
                }

                var unusedInfo = RemoveObjectFromPool(_objectsUnusedPool, key, obj, false);
                if (null != unusedInfo)
                {
                    var objsWithUnused = unusedInfo.value;
                    RemoveObjectToKeyReference(_unUsedObjectToKeys, unusedInfo);
                    if (null != objsWithUnused)
                    {
                        destorySuccess = true;
                        GetObjectSpawn().DestroyObject(objsWithUnused);
                    }
                }

                if (!destorySuccess)
                    Log.Error("ObjectPool DestroyObject error: not found object in pool, obj=" + obj + " key=" + key);
                else
                {
                    CheckObjectPoolDataOnRecycle(obj);
                }
            }
            return obj;
        }

        public void Clear()
        {
            lock (_mutex)
            {
                UnloadUnusedPoolData();

                foreach (var iter in _objectsInstantiatePool)
                {
                    foreach (var obj in iter.Value)
                        GetObjectSpawn().DestroyObject(obj.value);
                }

                _objectsInstantiatePool.Clear();
                _instantiateObjectToKeys.Clear();
            }
            System.GC.Collect();
        }

        //判断对象组是否为没有使用状态
        public bool isUnused(string key)
        {
            return _objectsUnusedPool.ContainsKey(key);
        }

        //判断对象是否为没有使用状态
        public bool isUnused(object obj)
        {
            return _unUsedObjectToKeys.ContainsKey(obj);
        }

        //判断对象组是否从内存池实例化出来了
        public bool IsInstantiated(string key)
        {
            return _objectsInstantiatePool.ContainsKey(key);
        }

        /// 判断对象是否从内存池实例化出来了
        public bool IsInstantiated(object obj)
        {
            return _instantiateObjectToKeys.ContainsKey(obj);
        }

        //判断对象是否在缓存池内
        public bool IsInPool(string key)
        {
            return IsInstantiated(key) || isUnused(key);
        }

        //判断对象是否在缓存池内
        public bool IsInPool(object obj)
        {
            return IsInstantiated(obj) || isUnused(obj);
        }

        //获取没有使用的缓存对象数量
        public int GetUnusedCount(string key)
        {
            List<PoolDataInfo> findValue = null;
            if (_objectsUnusedPool.TryGetValue(key, out findValue))
                return findValue.Count;
            else
                return 0;
        }

        //获取已经实例化出来的对象数量
        public int GetInstantiatedCount(string key)
        {
            List<PoolDataInfo> findValue = null;
            if (_objectsUnusedPool.TryGetValue(key, out findValue))
                return findValue.Count;
            else
                return 0;
        }

        /// <summary>
        /// 检查内存对象销毁方法并自动调用
        /// </summary>
        private void CheckObjectPoolDataOnRecycle(object obj)
        {
            var convertObj = obj as IObjectPoolData;
            if (null != convertObj)
            {
                try
                {
                    convertObj.OnRecycle();
                }
                catch (System.Exception e)
                {
                    Log.Error("ObjectPool CheckObjectPoolDataOnRecycle exception: obj=" + obj + " e=" + e);
                }
            }
        }

        private void AddObjectToPool(Dictionary<string, List<PoolDataInfo>> objectsPool, string key, PoolDataInfo poolData)
        {
            lock (_mutex)
            {
                List<PoolDataInfo> objects = null;
                if (!objectsPool.TryGetValue(key, out objects))
                {
                    objects = _cachePoolDataInfo.Count > 0 ? _cachePoolDataInfo.Pop() : new List<PoolDataInfo>();
                    objectsPool.Add(key, objects);
                }
                objects.Add(poolData);
            }
        }

        private void AddObjectsToPool(Dictionary<string, List<PoolDataInfo>> objectsPool, string key, List<PoolDataInfo> poolData)
        {
            lock (_mutex)
            {
                List<PoolDataInfo> objects = null;
                if (!objectsPool.TryGetValue(key, out objects))
                {
                    objects = _cachePoolDataInfo.Count > 0 ? _cachePoolDataInfo.Pop() : new List<PoolDataInfo>();
                    objectsPool.Add(key, objects);
                }
                objects.AddRange(poolData);
            }
        }

        private void RemoveObjectToKeyReference(Dictionary<object, string> targetDic, PoolDataInfo removePoolDataInfo)
        {
            lock (_mutex)
            {
                if (null == removePoolDataInfo)
                {
                    Log.Error("ObjectPool RemoveObjectToKeyReference error: removePoolDataInfo is null");
                    return;
                }

                if (null == removePoolDataInfo.value)
                {
                    Log.Error("ObjectPool RemoveObjectToKeyReference error: removePoolDataInfo.value is null");
                    return;
                }

                if (!targetDic.ContainsKey(removePoolDataInfo.value))
                {
                    //如果类型为字符串且内容相等时候，它们的索引值也会相当，所以会出现找不到的报错
                    //该报错可以忽略不计
                    if (removePoolDataInfo.value.GetType() != typeof(string))
                        Log.Error("ObjectPool RemoveObjectToKeyReference error: not found data=" + removePoolDataInfo.value);
                    return;
                }

                targetDic.Remove(removePoolDataInfo.value);
            }
        }

        private void RemoveObjectsToKeyReference(Dictionary<object, string> targetDic, List<PoolDataInfo> removePoolDataInfo)
        {
            if (removePoolDataInfo.IsNullOrEmpty())
            {
                // Log.Error("ObjectPool RemoveObjectsToKeyReference error: removePoolDataInfo is empty");
                return;
            }

            for (int i = removePoolDataInfo.Count - 1; i >= 0; --i)
            {
                var infoTmp = removePoolDataInfo[i];

                if (null == infoTmp.value)
                {
                    Log.Error("ObjectPool RemoveObjectsToKeyReference error: removePoolDataInfo.value is null");
                    continue;
                }

                if (!targetDic.ContainsKey(infoTmp.value))
                {
                    //如果类型为字符串且内容相等时候，它们的索引值也会相当，所以会出现找不到的报错
                    //该报错可以忽略不计
                    if (infoTmp.value.GetType() != typeof(string))
                        Log.Error("ObjectPool RemoveObjectsToKeyReference error: not found data=" + infoTmp.value);
                    continue;
                }

                targetDic.Remove(infoTmp.value);
            }
        }

        private void UpdateObjectToKeyReference(Dictionary<object, string> targetDic, string key, PoolDataInfo newPoolDataInfo)
        {
            targetDic[newPoolDataInfo.value] = key;
        }

        private void UpdateObjectToKeyReferences(Dictionary<object, string> targetDic, string key, List<PoolDataInfo> newPoolDataInfo)
        {
            if (newPoolDataInfo.IsNullOrEmpty())
                return;

            for (int i = newPoolDataInfo.Count - 1; i >= 0; --i)
            {
                var infoTmp = newPoolDataInfo[i];
                targetDic[infoTmp.value] = key;
            }
        }

        private PoolDataInfo RemoveObjectFromPool(Dictionary<string, List<PoolDataInfo>> objectsPool, string key)
        {
            lock (_mutex)
            {
                PoolDataInfo retValue = null;
                List<PoolDataInfo> poolDatas = null;

                if (null == key)
                {
                    Log.Error("ObjectPool RemoveObjectFromPool: key is null");
                    return retValue;
                }

                if (!objectsPool.TryGetValue(key, out poolDatas))
                {
                    Log.Error("ObjectPool RemoveObjectFromPool: not found key=" + key);
                    return retValue;
                }

                if (poolDatas.IsNullOrEmpty())
                {
                    Log.Error("ObjectPool RemoveObjectFromPool: objects is empty");
                    _cachePoolDataInfo.Push(poolDatas);
                    objectsPool.Remove(key);
                    return retValue;
                }

                retValue = poolDatas[poolDatas.Count - 1];
                poolDatas.RemoveAt(poolDatas.Count - 1);

                if (poolDatas.IsNullOrEmpty())
                {
                    _cachePoolDataInfo.Push(poolDatas);
                    objectsPool.Remove(key);
                }
                return retValue;
            }
        }

        private List<PoolDataInfo> RemoveObjectsFromPool(Dictionary<string, List<PoolDataInfo>> objectsPool, string key, bool printErrorWhenNotFound)
        {
            lock (_mutex)
            {
                List<PoolDataInfo> poolDatas = null;

                if (null == key)
                {
                    Log.Error("ObjectPool RemoveObjectsFromPool: key is null");
                    return poolDatas;
                }

                if (!objectsPool.TryGetValue(key, out poolDatas))
                {
                    if (printErrorWhenNotFound)
                        Log.Error("ObjectPool RemoveObjectsFromPool: not found key=" + key);
                    return poolDatas;
                }

                if (poolDatas.IsNullOrEmpty())
                {
                    Log.Error("ObjectPool RemoveObjectsFromPool: objects is empty");
                    objectsPool.Remove(key);
                    return poolDatas;
                }

                objectsPool.Remove(key);
                return poolDatas;
            }
        }

        private PoolDataInfo RemoveObjectFromPool<T>(Dictionary<string, List<PoolDataInfo>> objectsPool, string key, T obj, bool printErrorWhenNotFound)
        {
            lock (_mutex)
            {
                PoolDataInfo retValue = null;
                List<PoolDataInfo> poolDatas = null;

                if (null == key)
                {
                    Log.Error(string.Format("ObjectPool RemoveObjectFromPool<{0}>: key is null", typeof(T)));
                    return retValue;
                }

                if (null == obj)
                {
                    Log.Error(string.Format("ObjectPool RemoveObjectFromPool<{0}>: object is null", typeof(T)));
                    return retValue;
                }

                if (!objectsPool.TryGetValue(key, out poolDatas))
                {
                    if (printErrorWhenNotFound)
                        Log.Error(string.Format("ObjectPool RemoveObjectFromPool<{0}>: not found key=" + key, typeof(T)));
                    return retValue;
                }

                for (int i = poolDatas.Count - 1; i >= 0; --i)
                {
                    var removeObjTmp = poolDatas[i];
                    if (obj.Equals(removeObjTmp.value))
                    {
                        retValue = removeObjTmp;
                        poolDatas.RemoveAt(i);
                        break;
                    }
                }

                if (printErrorWhenNotFound && null == retValue)
                {
                    Log.Error(string.Format("ObjectPool RemoveObjectFromPool<{0}>: not found object=" + obj + " key=" + key, typeof(T)));
                    return retValue;
                }

                if (poolDatas.IsNullOrEmpty())
                {
                    _cachePoolDataInfo.Push(poolDatas);
                    objectsPool.Remove(key);
                }
                return retValue;
            }
        }

        private void InstantiateBaseEnd(string key, PoolDataInfo instantiatePoolData)
        {
            AddObjectToPool(_objectsInstantiatePool, key, instantiatePoolData);
            UpdateObjectToKeyReference(_instantiateObjectToKeys, key, instantiatePoolData);
        }

        private string ObjectToKey(object obj)
        {
            lock (_mutex)
            {
                if (null == obj)
                {
                    Log.Error("ObjectPool ObjectToKey error: object is null");
                    return string.Empty;
                }
                else
                {
                    var retValue = string.Empty;
                    if (!_instantiateObjectToKeys.TryGetValue(obj, out retValue))
                    {
                        if (!_unUsedObjectToKeys.TryGetValue(obj, out retValue))
                            Log.Error("ObjectPool ObjectToKey error: not found key by object=" + obj + " in pool=" + IsInPool(obj));
                    }
                    return retValue;
                }
            }
        }

        //获取对象生成器
        private shaco.Base.IObjectSpawn GetObjectSpawn()
        {
            return shaco.Base.GameHelper.objectpoolSpawn;
        }

        private void InstantiateAsyncWithCache<T>(string key, in List<PoolDataInfo> findObjectsList, in System.Action<System.Action<T>> callbackCreate, in System.Action<T> callbackEnd)
        {
            T retValue = default(T);
            var instantiatePoolData = findObjectsList[findObjectsList.Count - 1];

            if (isOpendStackLocation)
            {
                instantiatePoolData.CheckStackValid();
                instantiatePoolData.stackLocationInstantiate.GetStack();
                instantiatePoolData.stackLocationInstantiate.StartTimeSpanCalculate();
            }
            retValue = (T)instantiatePoolData.value;

            var removedPoolData = RemoveObjectFromPool(_objectsUnusedPool, key);
            RemoveObjectToKeyReference(_unUsedObjectToKeys, removedPoolData);
            GetObjectSpawn().ActiveObject(retValue);

            InstantiateBaseEnd(key, instantiatePoolData);
            if (isOpendStackLocation)
            {
                instantiatePoolData.stackLocationInstantiate.StopTimeSpanCalculate();
            }

            try
            {
                callbackEnd(retValue);
            }
            catch (System.Exception e)
            {
                Log.Error("ObjectPool InstantiateBase error: 3 key=" + key + " create=" + callbackCreate + " callbackEnd=" + callbackEnd + " e=" + e);
            }
        }

        private void InstantiateAsyncWithNew<T>(string key, in System.Action<System.Action<T>> callbackCreate, in System.Action<T> callbackEnd)
        {
            var instantiatePoolData = new PoolDataInfo();
            if (isOpendStackLocation)
            {
                instantiatePoolData.CheckStackValid();
                instantiatePoolData.stackLocationInstantiate.GetStack();
                instantiatePoolData.stackLocationInstantiate.StartTimeSpanCalculate();
            }

            var callbackCreateTmp = callbackCreate;
            var callbackEndTmp = callbackEnd;
            GetObjectSpawn().CreateNewObjectAsync<T>(callbackCreate, (newValue) =>
            {
                if (null == newValue)
                {
                    Log.Error("ObjectPool InstantiateBase error: create value is null, type=" + key + " key=" + key);
                    try
                    {
                        callbackEndTmp(newValue);
                    }
                    catch (System.Exception e)
                    {
                        Log.Error("ObjectPool InstantiateBase error: 1 key=" + key + " create=" + callbackCreateTmp + " callbackEnd=" + callbackEndTmp + " e=" + e);
                    }
                }
                else
                {
                    instantiatePoolData.value = newValue;
                    InstantiateBaseEnd(key, instantiatePoolData);
                    if (isOpendStackLocation)
                    {
                        instantiatePoolData.stackLocationInstantiate.StopTimeSpanCalculate();
                    }

                    try
                    {
                        callbackEndTmp(newValue);
                    }
                    catch (System.Exception e)
                    {
                        Log.Error("ObjectPool InstantiateBase error: 2 key=" + key + " create=" + callbackCreateTmp + " callbackEnd=" + callbackEndTmp + " e=" + e);
                    }
                }
            });
        }
    }
}